{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Pandas 时间序列重采样完整教程\n",
        "\n",
        "## 目录\n",
        "1. [重采样基础概念](#1-重采样基础概念)\n",
        "2. [resample()方法详解](#2-resample方法详解)\n",
        "3. [下采样（降频）](#3-下采样降频)\n",
        "4. [上采样（升频）](#4-上采样升频)\n",
        "5. [常用聚合函数](#5-常用聚合函数)\n",
        "6. [自定义聚合函数](#6-自定义聚合函数)\n",
        "7. [分组重采样](#7-分组重采样)\n",
        "8. [边界和标签控制](#8-边界和标签控制)\n",
        "9. [频率字符串详解](#9-频率字符串详解)\n",
        "10. [实际应用案例](#10-实际应用案例)\n",
        "11. [性能优化和注意事项](#11-性能优化和注意事项)\n",
        "\n",
        "---\n",
        "\n",
        "**重采样（Resampling）**是时间序列分析中的核心操作，用于改变时间序列的频率。主要包括：\n",
        "- **下采样（Downsampling）**：从高频数据转换为低频数据（如小时→日）\n",
        "- **上采样（Upsampling）**：从低频数据转换为高频数据（如日→小时）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "import pandas as pd\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import warnings\n",
        "warnings.filterwarnings('ignore')\n",
        "\n",
        "# 设置中文字体（如果需要）\n",
        "plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']\n",
        "plt.rcParams['axes.unicode_minus'] = False\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 1. 重采样基础概念\n",
        "\n",
        "### 1.1 什么是重采样？\n",
        "\n",
        "重采样是指将时间序列数据从一个频率转换到另一个频率的过程。\n",
        "\n",
        "### 1.2 两种重采样类型\n",
        "\n",
        "- **下采样（Downsampling）**：降低频率，数据点减少\n",
        "  - 例如：每小时数据 → 每日数据\n",
        "  - 需要聚合函数（mean, sum, max等）\n",
        "\n",
        "- **上采样（Upsampling）**：提高频率，数据点增加\n",
        "  - 例如：每日数据 → 每小时数据\n",
        "  - 需要填充方法（前向填充、插值等）\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建示例时间序列数据\n",
        "dates = pd.date_range('2023-01-01', periods=100, freq='H')  # 每小时数据\n",
        "ts = pd.Series(np.random.randn(100).cumsum(), index=dates)\n",
        "\n",
        "print(\"原始时间序列（每小时）:\")\n",
        "print(f\"数据点数: {len(ts)}\")\n",
        "print(f\"数据频率: {ts.index.freq}\")\n",
        "print(f\"时间范围: {ts.index[0]} 到 {ts.index[-1]}\")\n",
        "print(\"\\n前10个数据点:\")\n",
        "print(ts.head(10))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 下采样示例：每小时 → 每日\n",
        "daily_mean = ts.resample('D').mean()\n",
        "print(\"下采样到日频率（均值聚合）:\")\n",
        "print(f\"数据点数: {len(daily_mean)} (从 {len(ts)} 减少到 {len(daily_mean)})\")\n",
        "print(daily_mean.head())\n",
        "\n",
        "# 上采样示例：每日 → 每小时\n",
        "daily_ts = ts.resample('D').mean()  # 先下采样到日\n",
        "hourly_upsampled = daily_ts.resample('H').ffill()  # 再上采样到小时\n",
        "print(\"\\n上采样到小时频率（前向填充）:\")\n",
        "print(f\"数据点数: {len(hourly_upsampled)} (从 {len(daily_ts)} 增加到 {len(hourly_upsampled)})\")\n",
        "print(hourly_upsampled.head(10))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 2. resample()方法详解\n",
        "\n",
        "### 2.1 基本语法\n",
        "\n",
        "```python\n",
        "Series.resample(rule, axis=0, closed=None, label=None, convention='start', \n",
        "                kind=None, loffset=None, base=None, on=None, level=None, \n",
        "                origin='start_day', offset=None)\n",
        "```\n",
        "\n",
        "### 2.2 主要参数\n",
        "\n",
        "- **rule**: 频率字符串（如 'D', 'H', 'W'等）\n",
        "- **closed**: 区间闭合方向（'left' 或 'right'）\n",
        "- **label**: 标签位置（'left' 或 'right'）\n",
        "- **origin**: 时间窗口的起始点\n",
        "- **offset**: 时间偏移量\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# resample()返回一个DatetimeIndexResampler对象\n",
        "dates = pd.date_range('2023-01-01', periods=24, freq='H')\n",
        "ts = pd.Series(range(24), index=dates)\n",
        "\n",
        "resampler = ts.resample('D')\n",
        "print(f\"resample()返回类型: {type(resampler)}\")\n",
        "print(f\"\\n可用的聚合方法: {[m for m in dir(resampler) if not m.startswith('_')][:10]}...\")\n",
        "\n",
        "# 应用聚合函数\n",
        "result = resampler.mean()\n",
        "print(f\"\\n应用mean()后类型: {type(result)}\")\n",
        "print(result)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 3. 下采样（降频）\n",
        "\n",
        "下采样是将高频数据聚合为低频数据的过程，必须使用聚合函数。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建高频数据（每小时）\n",
        "np.random.seed(42)\n",
        "dates = pd.date_range('2023-01-01', periods=240, freq='H')  # 10天的每小时数据\n",
        "ts = pd.Series(np.random.randn(240).cumsum(), index=dates)\n",
        "\n",
        "print(\"原始数据（每小时）:\")\n",
        "print(f\"数据点数: {len(ts)}\")\n",
        "print(ts.head())\n",
        "\n",
        "# 下采样到不同频率\n",
        "resampling_examples = {\n",
        "    '6H': '6小时',\n",
        "    '12H': '12小时',\n",
        "    'D': '日',\n",
        "    'W': '周',\n",
        "    'M': '月'\n",
        "}\n",
        "\n",
        "print(\"\\n不同频率的下采样结果（使用mean聚合）:\")\n",
        "for freq, desc in resampling_examples.items():\n",
        "    resampled = ts.resample(freq).mean()\n",
        "    print(f\"{desc:6} ({freq:3}): {len(resampled):3}个数据点\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 下采样到日频率，使用不同聚合函数\n",
        "daily_stats = pd.DataFrame({\n",
        "    'mean': ts.resample('D').mean(),\n",
        "    'sum': ts.resample('D').sum(),\n",
        "    'max': ts.resample('D').max(),\n",
        "    'min': ts.resample('D').min(),\n",
        "    'std': ts.resample('D').std(),\n",
        "    'median': ts.resample('D').median(),\n",
        "    'count': ts.resample('D').count(),\n",
        "    'first': ts.resample('D').first(),\n",
        "    'last': ts.resample('D').last()\n",
        "})\n",
        "\n",
        "print(\"日频率统计（多种聚合函数）:\")\n",
        "print(daily_stats.head(10))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化下采样效果\n",
        "fig, axes = plt.subplots(3, 1, figsize=(12, 10))\n",
        "\n",
        "# 原始小时数据（前48小时）\n",
        "ts[:48].plot(ax=axes[0], title='原始数据（每小时，前48小时）', color='blue', alpha=0.7)\n",
        "axes[0].set_ylabel('值')\n",
        "axes[0].grid(True, alpha=0.3)\n",
        "\n",
        "# 下采样到6小时\n",
        "ts.resample('6H').mean()[:8].plot(ax=axes[1], title='下采样到6小时（均值）', \n",
        "                                  color='green', marker='o')\n",
        "axes[1].set_ylabel('值')\n",
        "axes[1].grid(True, alpha=0.3)\n",
        "\n",
        "# 下采样到日\n",
        "ts.resample('D').mean()[:5].plot(ax=axes[2], title='下采样到日（均值）', \n",
        "                                 color='red', marker='s')\n",
        "axes[2].set_ylabel('值')\n",
        "axes[2].grid(True, alpha=0.3)\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 4. 上采样（升频）\n",
        "\n",
        "上采样是将低频数据转换为高频数据的过程，需要填充缺失值。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建低频数据（每日）\n",
        "daily_dates = pd.date_range('2023-01-01', periods=10, freq='D')\n",
        "daily_ts = pd.Series(np.random.randn(10).cumsum(), index=daily_dates)\n",
        "\n",
        "print(\"原始日频率数据:\")\n",
        "print(f\"数据点数: {len(daily_ts)}\")\n",
        "print(daily_ts)\n",
        "\n",
        "# 上采样到小时频率（不填充，显示NaN）\n",
        "hourly_asfreq = daily_ts.resample('H').asfreq()\n",
        "print(f\"\\n上采样到小时频率（asfreq，不填充）:\")\n",
        "print(f\"数据点数: {len(hourly_asfreq)}\")\n",
        "print(f\"非空值数量: {hourly_asfreq.notna().sum()}\")\n",
        "print(hourly_asfreq.head(25))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 上采样的不同填充方法\n",
        "print(\"上采样填充方法对比:\")\n",
        "\n",
        "# 1. 前向填充（forward fill）- 用前一个值填充\n",
        "ffill_result = daily_ts.resample('6H').ffill()\n",
        "print(\"\\n1. 前向填充（ffill）:\")\n",
        "print(ffill_result.head(10))\n",
        "\n",
        "# 2. 后向填充（backward fill）- 用后一个值填充\n",
        "bfill_result = daily_ts.resample('6H').bfill()\n",
        "print(\"\\n2. 后向填充（bfill）:\")\n",
        "print(bfill_result.head(10))\n",
        "\n",
        "# 3. 线性插值\n",
        "interpolated = daily_ts.resample('6H').interpolate(method='linear')\n",
        "print(\"\\n3. 线性插值（interpolate）:\")\n",
        "print(interpolated.head(10))\n",
        "\n",
        "# 4. 填充固定值\n",
        "filled_zero = daily_ts.resample('6H').asfreq().fillna(0)\n",
        "print(\"\\n4. 填充0值:\")\n",
        "print(filled_zero.head(10))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化不同填充方法的效果\n",
        "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
        "\n",
        "# 原始日数据\n",
        "daily_ts.plot(ax=axes[0,0], marker='o', title='原始日数据', linewidth=2, markersize=8)\n",
        "axes[0,0].set_ylabel('值')\n",
        "axes[0,0].grid(True, alpha=0.3)\n",
        "\n",
        "# 前向填充\n",
        "ffill_result.plot(ax=axes[0,1], title='前向填充（ffill）', alpha=0.7, color='green')\n",
        "daily_ts.plot(ax=axes[0,1], marker='o', color='red', markersize=8, linewidth=0)\n",
        "axes[0,1].set_ylabel('值')\n",
        "axes[0,1].grid(True, alpha=0.3)\n",
        "axes[0,1].legend(['填充值', '原始值'])\n",
        "\n",
        "# 后向填充\n",
        "bfill_result.plot(ax=axes[1,0], title='后向填充（bfill）', alpha=0.7, color='blue')\n",
        "daily_ts.plot(ax=axes[1,0], marker='o', color='red', markersize=8, linewidth=0)\n",
        "axes[1,0].set_ylabel('值')\n",
        "axes[1,0].grid(True, alpha=0.3)\n",
        "axes[1,0].legend(['填充值', '原始值'])\n",
        "\n",
        "# 线性插值\n",
        "interpolated.plot(ax=axes[1,1], title='线性插值（interpolate）', alpha=0.7, color='purple')\n",
        "daily_ts.plot(ax=axes[1,1], marker='o', color='red', markersize=8, linewidth=0)\n",
        "axes[1,1].set_ylabel('值')\n",
        "axes[1,1].grid(True, alpha=0.3)\n",
        "axes[1,1].legend(['插值', '原始值'])\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 5. 常用聚合函数\n",
        "\n",
        "下采样时必须使用聚合函数，Pandas提供了丰富的内置聚合函数。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建示例数据\n",
        "dates = pd.date_range('2023-01-01', periods=100, freq='H')\n",
        "ts = pd.Series(np.random.randn(100).cumsum() + 100, index=dates)\n",
        "\n",
        "print(\"常用聚合函数示例:\")\n",
        "print(\"=\" * 60)\n",
        "\n",
        "aggregations = {\n",
        "    'mean': ts.resample('D').mean(),\n",
        "    'sum': ts.resample('D').sum(),\n",
        "    'max': ts.resample('D').max(),\n",
        "    'min': ts.resample('D').min(),\n",
        "    'std': ts.resample('D').std(),\n",
        "    'var': ts.resample('D').var(),\n",
        "    'median': ts.resample('D').median(),\n",
        "    'count': ts.resample('D').count(),\n",
        "    'first': ts.resample('D').first(),\n",
        "    'last': ts.resample('D').last(),\n",
        "    'nunique': ts.resample('D').nunique(),\n",
        "    'size': ts.resample('D').size()\n",
        "}\n",
        "\n",
        "result_df = pd.DataFrame(aggregations)\n",
        "print(result_df.head())\n",
        "\n",
        "print(\"\\n聚合函数说明:\")\n",
        "print(\"- mean: 平均值\")\n",
        "print(\"- sum: 求和\")\n",
        "print(\"- max/min: 最大值/最小值\")\n",
        "print(\"- std/var: 标准差/方差\")\n",
        "print(\"- median: 中位数\")\n",
        "print(\"- count: 非空值计数\")\n",
        "print(\"- size: 总计数（包括NaN）\")\n",
        "print(\"- first/last: 第一个/最后一个值\")\n",
        "print(\"- nunique: 唯一值数量\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 使用agg()方法进行多重聚合\n",
        "multi_agg = ts.resample('D').agg(['mean', 'std', 'min', 'max', 'count'])\n",
        "print(\"使用agg()进行多重聚合:\")\n",
        "print(multi_agg.head())\n",
        "\n",
        "# 重命名聚合列\n",
        "named_agg = ts.resample('D').agg([\n",
        "    ('平均值', 'mean'),\n",
        "    ('标准差', 'std'),\n",
        "    ('最大值', 'max'),\n",
        "    ('最小值', 'min'),\n",
        "    ('数据点数', 'count')\n",
        "])\n",
        "\n",
        "print(\"\\n重命名聚合列:\")\n",
        "print(named_agg.head())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 6. 自定义聚合函数\n",
        "\n",
        "可以使用自定义函数进行更复杂的聚合操作。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建温度数据示例\n",
        "dates = pd.date_range('2023-01-01', periods=200, freq='H')\n",
        "values = np.random.randn(200) * 5 + 25  # 模拟温度数据（25度左右）\n",
        "temp_ts = pd.Series(values, index=dates)\n",
        "\n",
        "# 自定义聚合函数\n",
        "def temperature_range(x):\n",
        "    \"\"\"计算温度范围（最大值-最小值）\"\"\"\n",
        "    return x.max() - x.min()\n",
        "\n",
        "def above_threshold_count(x, threshold=25):\n",
        "    \"\"\"统计超过阈值的数量\"\"\"\n",
        "    return (x > threshold).sum()\n",
        "\n",
        "def temperature_volatility(x):\n",
        "    \"\"\"计算温度波动性（变异系数）\"\"\"\n",
        "    if x.std() == 0:\n",
        "        return 0\n",
        "    return x.std() / x.mean()\n",
        "\n",
        "# 应用自定义函数\n",
        "daily_analysis = pd.DataFrame({\n",
        "    'avg_temp': temp_ts.resample('D').mean(),\n",
        "    'max_temp': temp_ts.resample('D').max(),\n",
        "    'min_temp': temp_ts.resample('D').min(),\n",
        "    'temp_range': temp_ts.resample('D').apply(temperature_range),\n",
        "    'hours_above_25': temp_ts.resample('D').apply(lambda x: above_threshold_count(x, 25)),\n",
        "    'volatility': temp_ts.resample('D').apply(temperature_volatility)\n",
        "})\n",
        "\n",
        "print(\"每日温度分析（使用自定义函数）:\")\n",
        "print(daily_analysis.head(10).round(2))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 使用agg()结合自定义函数\n",
        "custom_agg = temp_ts.resample('D').agg({\n",
        "    'mean': 'mean',\n",
        "    'std': 'std',\n",
        "    'range': lambda x: x.max() - x.min(),\n",
        "    'median': 'median',\n",
        "    'count': 'count',\n",
        "    'q25': lambda x: x.quantile(0.25),  # 25分位数\n",
        "    'q75': lambda x: x.quantile(0.75)   # 75分位数\n",
        "})\n",
        "\n",
        "print(\"使用agg()结合自定义函数:\")\n",
        "print(custom_agg.head().round(2))\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 7. 分组重采样\n",
        "\n",
        "可以结合groupby()对不同的组进行重采样。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建多组时间序列数据\n",
        "dates = pd.date_range('2023-01-01', periods=200, freq='H')\n",
        "df = pd.DataFrame({\n",
        "    'temperature': np.random.randn(200) * 5 + 20,\n",
        "    'humidity': np.random.randn(200) * 10 + 60,\n",
        "    'pressure': np.random.randn(200) * 2 + 1013,\n",
        "    'city': np.random.choice(['北京', '上海', '广州'], 200)\n",
        "}, index=dates)\n",
        "\n",
        "print(\"原始气象数据:\")\n",
        "print(df.head(10))\n",
        "print(f\"\\n数据形状: {df.shape}\")\n",
        "print(f\"城市分布: {df['city'].value_counts().to_dict()}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 按城市分组后重采样\n",
        "city_daily = df.groupby('city').resample('D').mean()\n",
        "print(\"按城市分组的日均值:\")\n",
        "print(city_daily.head(15))\n",
        "\n",
        "# 查看每个城市的统计\n",
        "print(\"\\n各城市日均温度统计:\")\n",
        "print(city_daily.groupby('city')['temperature'].agg(['mean', 'std', 'min', 'max']).round(2))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 不同列使用不同聚合函数\n",
        "weather_summary = df.groupby('city').resample('D').agg({\n",
        "    'temperature': ['mean', 'max', 'min'],\n",
        "    'humidity': 'mean',\n",
        "    'pressure': ['mean', 'std']\n",
        "})\n",
        "\n",
        "print(\"分组多重聚合（多级列索引）:\")\n",
        "print(weather_summary.head())\n",
        "\n",
        "# 展平多级列索引\n",
        "weather_summary.columns = ['_'.join(col).strip() for col in weather_summary.columns.values]\n",
        "print(\"\\n展平列索引后:\")\n",
        "print(weather_summary.head())\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化分组重采样结果\n",
        "fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
        "\n",
        "# 各城市日均温度\n",
        "city_daily['temperature'].unstack('city').plot(ax=axes[0,0], title='各城市日均温度')\n",
        "axes[0,0].set_ylabel('温度 (°C)')\n",
        "axes[0,0].grid(True, alpha=0.3)\n",
        "axes[0,0].legend(title='城市')\n",
        "\n",
        "# 各城市日均湿度\n",
        "city_daily['humidity'].unstack('city').plot(ax=axes[0,1], title='各城市日均湿度')\n",
        "axes[0,1].set_ylabel('湿度 (%)')\n",
        "axes[0,1].grid(True, alpha=0.3)\n",
        "axes[0,1].legend(title='城市')\n",
        "\n",
        "# 各城市温度范围（最大值-最小值）\n",
        "temp_range = weather_summary['temperature_max'] - weather_summary['temperature_min']\n",
        "temp_range.unstack('city').plot(ax=axes[1,0], title='各城市日温度范围', kind='bar')\n",
        "axes[1,0].set_ylabel('温度范围 (°C)')\n",
        "axes[1,0].tick_params(axis='x', rotation=45)\n",
        "axes[1,0].legend(title='城市')\n",
        "\n",
        "# 各城市平均气压\n",
        "weather_summary['pressure_mean'].unstack('city').plot(ax=axes[1,1], title='各城市日均气压')\n",
        "axes[1,1].set_ylabel('气压 (hPa)')\n",
        "axes[1,1].grid(True, alpha=0.3)\n",
        "axes[1,1].legend(title='城市')\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 8. 边界和标签控制\n",
        "\n",
        "重采样时可以通过`label`和`closed`参数控制时间窗口的边界和标签位置。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建示例数据\n",
        "dates = pd.date_range('2023-01-01 00:00', periods=25, freq='H')\n",
        "ts = pd.Series(range(25), index=dates)\n",
        "\n",
        "print(\"原始数据（前10个）:\")\n",
        "print(ts.head(10))\n",
        "print(f\"\\n数据范围: {ts.index[0]} 到 {ts.index[-1]}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# label参数：控制标签位置\n",
        "print(\"label参数说明:\")\n",
        "print(\"- label='left' (默认): 使用时间窗口的左边界作为标签\")\n",
        "print(\"- label='right': 使用时间窗口的右边界作为标签\")\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "\n",
        "# 默认（左边界标签）\n",
        "left_label = ts.resample('6H', label='left').sum()\n",
        "print(\"\\n左边界标签 (label='left'):\")\n",
        "print(left_label)\n",
        "\n",
        "# 右边界标签\n",
        "right_label = ts.resample('6H', label='right').sum()\n",
        "print(\"\\n右边界标签 (label='right'):\")\n",
        "print(right_label)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# closed参数：控制区间闭合方向\n",
        "print(\"closed参数说明:\")\n",
        "print(\"- closed='left' (默认): 左闭右开区间 [start, end)\")\n",
        "print(\"- closed='right': 左开右闭区间 (start, end]\")\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "\n",
        "# 左闭合（默认）\n",
        "closed_left = ts.resample('6H', closed='left').sum()\n",
        "print(\"\\n左闭合区间 (closed='left'):\")\n",
        "print(closed_left)\n",
        "\n",
        "# 右闭合\n",
        "closed_right = ts.resample('6H', closed='right').sum()\n",
        "print(\"\\n右闭合区间 (closed='right'):\")\n",
        "print(closed_right)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# origin参数：控制时间窗口的起始点\n",
        "print(\"origin参数说明:\")\n",
        "print(\"- origin='start_day' (默认): 从数据的第一天开始\")\n",
        "print(\"- origin='epoch': 从1970-01-01开始\")\n",
        "print(\"- origin=Timestamp: 从指定时间开始\")\n",
        "print(\"\\n\" + \"=\"*60)\n",
        "\n",
        "# 默认origin\n",
        "default_origin = ts.resample('D', label='right').sum()\n",
        "print(\"\\n默认origin (start_day):\")\n",
        "print(default_origin)\n",
        "\n",
        "# 指定origin\n",
        "custom_origin = ts.resample('D', label='right', origin='2023-01-01 12:00').sum()\n",
        "print(\"\\n自定义origin (2023-01-01 12:00):\")\n",
        "print(custom_origin)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 9. 频率字符串详解\n",
        "\n",
        "Pandas支持丰富的频率字符串，用于指定重采样的目标频率。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 常用频率字符串\n",
        "print(\"常用频率字符串:\")\n",
        "print(\"=\" * 60)\n",
        "print(\"\\n时间频率:\")\n",
        "print(\"  'S'      -> 秒\")\n",
        "print(\"  'T'/'min' -> 分钟\")\n",
        "print(\"  'H'      -> 小时\")\n",
        "print(\"  'D'      -> 日\")\n",
        "print(\"  'W'      -> 周\")\n",
        "print(\"  'M'      -> 月末\")\n",
        "print(\"  'Q'      -> 季度末\")\n",
        "print(\"  'Y'/'A'  -> 年末\")\n",
        "print(\"\\n工作日频率:\")\n",
        "print(\"  'B'      -> 工作日\")\n",
        "print(\"  'BM'     -> 月末工作日\")\n",
        "print(\"  'BQ'     -> 季度末工作日\")\n",
        "print(\"  'BY'     -> 年末工作日\")\n",
        "print(\"\\n自定义频率:\")\n",
        "print(\"  '2H'     -> 2小时\")\n",
        "print(\"  '3D'     -> 3天\")\n",
        "print(\"  '2W'     -> 2周\")\n",
        "print(\"  'MS'     -> 月初\")\n",
        "print(\"  'QS'     -> 季度初\")\n",
        "print(\"  'YS'     -> 年初\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 创建示例数据测试不同频率\n",
        "dates = pd.date_range('2023-01-01', periods=1000, freq='H')\n",
        "ts = pd.Series(np.random.randn(1000).cumsum(), index=dates)\n",
        "\n",
        "print(\"不同频率重采样测试:\")\n",
        "print(\"=\" * 60)\n",
        "\n",
        "test_frequencies = ['H', '6H', '12H', 'D', 'W', 'M', 'Q', 'Y']\n",
        "\n",
        "for freq in test_frequencies:\n",
        "    try:\n",
        "        resampled = ts.resample(freq).mean()\n",
        "        print(f\"{freq:6} -> {len(resampled):4}个数据点, 首时间: {resampled.index[0]}\")\n",
        "    except Exception as e:\n",
        "        print(f\"{freq:6} -> 错误: {e}\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 工作日频率示例\n",
        "business_dates = pd.date_range('2023-01-01', periods=100, freq='B')  # 工作日\n",
        "business_ts = pd.Series(np.random.randn(100).cumsum(), index=business_dates)\n",
        "\n",
        "print(\"工作日数据:\")\n",
        "print(f\"数据点数: {len(business_ts)}\")\n",
        "print(business_ts.head())\n",
        "\n",
        "# 重采样到周（工作日周）\n",
        "weekly_business = business_ts.resample('W').mean()\n",
        "print(f\"\\n重采样到周（工作日）: {len(weekly_business)}个数据点\")\n",
        "print(weekly_business.head())\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 10. 实际应用案例\n",
        "\n",
        "### 10.1 股票数据OHLCV转换\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 模拟股票交易数据（分钟级）\n",
        "np.random.seed(42)\n",
        "trading_dates = pd.date_range('2023-01-01 09:30', '2023-01-31 15:00', freq='T')\n",
        "n_points = len(trading_dates)\n",
        "\n",
        "# 模拟价格数据\n",
        "base_price = 100\n",
        "price_changes = np.random.randn(n_points) * 0.1\n",
        "prices = base_price + np.cumsum(price_changes)\n",
        "\n",
        "stock_data = pd.DataFrame({\n",
        "    'price': prices,\n",
        "    'volume': np.random.randint(100, 1000, n_points)\n",
        "}, index=trading_dates)\n",
        "\n",
        "print(f\"原始分钟数据点数: {len(stock_data)}\")\n",
        "print(stock_data.head())\n",
        "\n",
        "# 重采样为小时OHLCV格式\n",
        "ohlcv_hourly = stock_data['price'].resample('H').agg({\n",
        "    'open': 'first',\n",
        "    'high': 'max',\n",
        "    'low': 'min',\n",
        "    'close': 'last'\n",
        "})\n",
        "\n",
        "# 添加成交量\n",
        "ohlcv_hourly['volume'] = stock_data['volume'].resample('H').sum()\n",
        "\n",
        "print(f\"\\n小时OHLCV数据点数: {len(ohlcv_hourly)}\")\n",
        "print(ohlcv_hourly.head(10))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 计算技术指标\n",
        "ohlcv_hourly['returns'] = ohlcv_hourly['close'].pct_change()\n",
        "ohlcv_hourly['volatility'] = ohlcv_hourly['returns'].rolling(24).std()  # 24小时滚动波动率\n",
        "ohlcv_hourly['avg_volume'] = ohlcv_hourly['volume'].rolling(24).mean()\n",
        "ohlcv_hourly['price_change'] = ohlcv_hourly['close'] - ohlcv_hourly['open']\n",
        "\n",
        "print(\"添加技术指标后的OHLCV数据:\")\n",
        "print(ohlcv_hourly[['open', 'high', 'low', 'close', 'volume', 'returns', 'volatility']].tail(10).round(4))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化股票数据重采样结果\n",
        "fig, axes = plt.subplots(3, 1, figsize=(14, 12))\n",
        "\n",
        "# 原始分钟价格（前1000点）\n",
        "stock_data['price'][:1000].plot(ax=axes[0], title='原始分钟价格数据（前1000点）', \n",
        "                                 color='blue', alpha=0.6, linewidth=0.5)\n",
        "axes[0].set_ylabel('价格')\n",
        "axes[0].grid(True, alpha=0.3)\n",
        "\n",
        "# 小时OHLCV（K线图风格）\n",
        "ohlcv_hourly['close'][:100].plot(ax=axes[1], title='小时收盘价（前100小时）', \n",
        "                                 color='green', marker='o', markersize=3)\n",
        "axes[1].set_ylabel('价格')\n",
        "axes[1].grid(True, alpha=0.3)\n",
        "\n",
        "# 日成交量\n",
        "daily_volume = stock_data['volume'].resample('D').sum()\n",
        "daily_volume[:30].plot(kind='bar', ax=axes[2], title='日成交量（前30天）', color='orange')\n",
        "axes[2].set_ylabel('成交量')\n",
        "axes[2].tick_params(axis='x', rotation=45)\n",
        "axes[2].grid(True, alpha=0.3, axis='y')\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 10.2 传感器数据聚合分析\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 模拟传感器数据（每秒采集）\n",
        "np.random.seed(123)\n",
        "sensor_dates = pd.date_range('2023-01-01', periods=86400, freq='S')  # 一天的秒数据\n",
        "n_points = len(sensor_dates)\n",
        "\n",
        "sensor_data = pd.DataFrame({\n",
        "    'temperature': 20 + np.sin(np.arange(n_points) / 3600 * 2 * np.pi) * 5 + np.random.randn(n_points) * 0.5,\n",
        "    'humidity': 50 + np.random.randn(n_points) * 3,\n",
        "    'pressure': 1013 + np.random.randn(n_points) * 1\n",
        "}, index=sensor_dates)\n",
        "\n",
        "print(f\"原始秒级数据点数: {len(sensor_data)}\")\n",
        "print(sensor_data.head())\n",
        "\n",
        "# 重采样到不同频率\n",
        "sensor_minute = sensor_data.resample('T').mean()  # 分钟平均\n",
        "sensor_hour = sensor_data.resample('H').agg({\n",
        "    'temperature': ['mean', 'max', 'min', 'std'],\n",
        "    'humidity': ['mean', 'max', 'min'],\n",
        "    'pressure': ['mean', 'std']\n",
        "})\n",
        "\n",
        "print(f\"\\n分钟级数据点数: {len(sensor_minute)}\")\n",
        "print(sensor_minute.head())\n",
        "\n",
        "print(f\"\\n小时级统计:\")\n",
        "print(sensor_hour.head())\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化传感器数据重采样\n",
        "fig, axes = plt.subplots(2, 2, figsize=(15, 10))\n",
        "\n",
        "# 原始秒级温度（前1000秒）\n",
        "sensor_data['temperature'][:1000].plot(ax=axes[0,0], title='原始秒级温度（前1000秒）', \n",
        "                                        color='red', alpha=0.5, linewidth=0.3)\n",
        "axes[0,0].set_ylabel('温度 (°C)')\n",
        "axes[0,0].grid(True, alpha=0.3)\n",
        "\n",
        "# 分钟平均温度\n",
        "sensor_minute['temperature'].plot(ax=axes[0,1], title='分钟平均温度', color='red', linewidth=1.5)\n",
        "axes[0,1].set_ylabel('温度 (°C)')\n",
        "axes[0,1].grid(True, alpha=0.3)\n",
        "\n",
        "# 小时温度统计\n",
        "sensor_hour['temperature']['mean'].plot(ax=axes[1,0], title='小时平均温度', \n",
        "                                        color='blue', marker='o', markersize=4)\n",
        "sensor_hour['temperature']['max'].plot(ax=axes[1,0], color='red', linestyle='--', alpha=0.7)\n",
        "sensor_hour['temperature']['min'].plot(ax=axes[1,0], color='green', linestyle='--', alpha=0.7)\n",
        "axes[1,0].set_ylabel('温度 (°C)')\n",
        "axes[1,0].legend(['平均', '最大', '最小'])\n",
        "axes[1,0].grid(True, alpha=0.3)\n",
        "\n",
        "# 小时湿度\n",
        "sensor_hour['humidity']['mean'].plot(ax=axes[1,1], title='小时平均湿度', \n",
        "                                     color='cyan', marker='s', markersize=4)\n",
        "axes[1,1].set_ylabel('湿度 (%)')\n",
        "axes[1,1].grid(True, alpha=0.3)\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 10.3 销售数据多频率分析\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 模拟销售数据（每日）\n",
        "np.random.seed(456)\n",
        "sales_dates = pd.date_range('2023-01-01', '2023-12-31', freq='D')\n",
        "n_days = len(sales_dates)\n",
        "\n",
        "# 模拟有趋势和季节性的销售数据\n",
        "trend = np.linspace(1000, 1500, n_days)\n",
        "seasonal = 200 * np.sin(2 * np.pi * np.arange(n_days) / 365.25)\n",
        "noise = np.random.randn(n_days) * 50\n",
        "sales = trend + seasonal + noise\n",
        "\n",
        "sales_df = pd.DataFrame({\n",
        "    'sales': sales,\n",
        "    'orders': (sales / 50).astype(int) + np.random.randint(-5, 5, n_days)\n",
        "}, index=sales_dates)\n",
        "\n",
        "print(f\"原始日销售数据点数: {len(sales_df)}\")\n",
        "print(sales_df.head())\n",
        "\n",
        "# 多频率分析\n",
        "weekly_sales = sales_df.resample('W').agg({\n",
        "    'sales': ['sum', 'mean', 'std'],\n",
        "    'orders': 'sum'\n",
        "})\n",
        "\n",
        "monthly_sales = sales_df.resample('M').agg({\n",
        "    'sales': ['sum', 'mean', 'max', 'min'],\n",
        "    'orders': 'sum'\n",
        "})\n",
        "\n",
        "quarterly_sales = sales_df.resample('Q').agg({\n",
        "    'sales': ['sum', 'mean'],\n",
        "    'orders': 'sum'\n",
        "})\n",
        "\n",
        "print(f\"\\n周销售汇总: {len(weekly_sales)}周\")\n",
        "print(weekly_sales.head())\n",
        "\n",
        "print(f\"\\n月销售汇总: {len(monthly_sales)}月\")\n",
        "print(monthly_sales.head())\n",
        "\n",
        "print(f\"\\n季度销售汇总: {len(quarterly_sales)}季度\")\n",
        "print(quarterly_sales)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 可视化多频率销售分析\n",
        "fig, axes = plt.subplots(2, 2, figsize=(15, 10))\n",
        "\n",
        "# 日销售数据（前90天）\n",
        "sales_df['sales'][:90].plot(ax=axes[0,0], title='日销售数据（前90天）', color='blue', alpha=0.7)\n",
        "axes[0,0].set_ylabel('销售额')\n",
        "axes[0,0].grid(True, alpha=0.3)\n",
        "\n",
        "# 周销售汇总\n",
        "weekly_sales['sales']['sum'].plot(ax=axes[0,1], title='周销售总额', \n",
        "                                 color='green', marker='o', markersize=4)\n",
        "axes[0,1].set_ylabel('销售额')\n",
        "axes[0,1].grid(True, alpha=0.3)\n",
        "\n",
        "# 月销售汇总\n",
        "monthly_sales['sales']['sum'].plot(kind='bar', ax=axes[1,0], title='月销售总额', color='orange')\n",
        "axes[1,0].set_ylabel('销售额')\n",
        "axes[1,0].tick_params(axis='x', rotation=45)\n",
        "axes[1,0].grid(True, alpha=0.3, axis='y')\n",
        "\n",
        "# 季度销售汇总\n",
        "quarterly_sales['sales']['sum'].plot(kind='bar', ax=axes[1,1], title='季度销售总额', color='red')\n",
        "axes[1,1].set_ylabel('销售额')\n",
        "axes[1,1].tick_params(axis='x', rotation=45)\n",
        "axes[1,1].grid(True, alpha=0.3, axis='y')\n",
        "\n",
        "plt.tight_layout()\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 11. 性能优化和注意事项\n",
        "\n",
        "### 11.1 性能优化技巧\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 1. 对于大数据集，使用更高效的聚合函数\n",
        "import time\n",
        "\n",
        "# 创建大数据集\n",
        "large_dates = pd.date_range('2023-01-01', periods=100000, freq='T')  # 分钟数据\n",
        "large_ts = pd.Series(np.random.randn(100000), index=large_dates)\n",
        "\n",
        "print(f\"大数据集: {len(large_ts):,}个数据点\")\n",
        "\n",
        "# 方法1: 多次调用resample（较慢）\n",
        "start = time.time()\n",
        "result1 = pd.DataFrame({\n",
        "    'mean': large_ts.resample('H').mean(),\n",
        "    'max': large_ts.resample('H').max(),\n",
        "    'min': large_ts.resample('H').min()\n",
        "})\n",
        "time1 = time.time() - start\n",
        "print(f\"方法1（多次resample）: {time1:.4f}秒\")\n",
        "\n",
        "# 方法2: 使用agg（更快）\n",
        "start = time.time()\n",
        "result2 = large_ts.resample('H').agg(['mean', 'max', 'min'])\n",
        "time2 = time.time() - start\n",
        "print(f\"方法2（使用agg）: {time2:.4f}秒\")\n",
        "print(f\"性能提升: {time1/time2:.2f}倍\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 2. 对于DataFrame，按列聚合比多次resample更高效\n",
        "large_df = pd.DataFrame({\n",
        "    'A': np.random.randn(100000),\n",
        "    'B': np.random.randn(100000),\n",
        "    'C': np.random.randn(100000)\n",
        "}, index=large_dates)\n",
        "\n",
        "# 高效方法：一次性对DataFrame重采样\n",
        "start = time.time()\n",
        "df_resampled = large_df.resample('H').mean()\n",
        "time_df = time.time() - start\n",
        "print(f\"DataFrame重采样: {time_df:.4f}秒\")\n",
        "print(f\"结果形状: {df_resampled.shape}\")\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### 11.2 常见注意事项\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "print(\"重采样注意事项:\")\n",
        "print(\"=\" * 60)\n",
        "print(\"\")\n",
        "print(\"1. 索引必须是DatetimeIndex\")\n",
        "print(\"   - 如果索引不是时间类型，需要先转换: df.index = pd.to_datetime(df.index)\")\n",
        "print(\"\")\n",
        "print(\"2. 下采样必须使用聚合函数\")\n",
        "print(\"   - 不能直接使用resample()，必须调用聚合方法如mean(), sum()等\")\n",
        "print(\"\")\n",
        "print(\"3. 上采样需要处理缺失值\")\n",
        "print(\"   - 使用ffill(), bfill(), interpolate()等方法填充\")\n",
        "print(\"\")\n",
        "print(\"4. 注意时区问题\")\n",
        "print(\"   - 如果数据有时区信息，重采样会保持时区\")\n",
        "print(\"\")\n",
        "print(\"5. 边界和标签的理解\")\n",
        "print(\"   - label控制标签位置，closed控制区间闭合方向\")\n",
        "print(\"\")\n",
        "print(\"6. 频率字符串的大小写敏感\")\n",
        "print(\"   - 'M'表示月末，'MS'表示月初\")\n",
        "print(\"   - 'Q'表示季度末，'QS'表示季度初\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 示例：处理非DatetimeIndex的情况\n",
        "df_with_str_index = pd.DataFrame({\n",
        "    'value': np.random.randn(10)\n",
        "}, index=['2023-01-01', '2023-01-02', '2023-01-03', '2023-01-04', '2023-01-05',\n",
        "          '2023-01-06', '2023-01-07', '2023-01-08', '2023-01-09', '2023-01-10'])\n",
        "\n",
        "print(\"原始数据（字符串索引）:\")\n",
        "print(df_with_str_index.head())\n",
        "print(f\"索引类型: {type(df_with_str_index.index)}\")\n",
        "\n",
        "# 转换为DatetimeIndex\n",
        "df_with_str_index.index = pd.to_datetime(df_with_str_index.index)\n",
        "print(f\"\\n转换后索引类型: {type(df_with_str_index.index)}\")\n",
        "\n",
        "# 现在可以重采样\n",
        "resampled = df_with_str_index.resample('2D').mean()\n",
        "print(\"\\n重采样结果:\")\n",
        "print(resampled)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
        "# 示例：时区处理\n",
        "dates_utc = pd.date_range('2023-01-01', periods=10, freq='H', tz='UTC')\n",
        "ts_utc = pd.Series(np.random.randn(10), index=dates_utc)\n",
        "\n",
        "print(\"UTC时区数据:\")\n",
        "print(ts_utc.head())\n",
        "print(f\"时区: {ts_utc.index.tz}\")\n",
        "\n",
        "# 重采样保持时区\n",
        "resampled_utc = ts_utc.resample('2H').mean()\n",
        "print(f\"\\n重采样后时区: {resampled_utc.index.tz}\")\n",
        "print(resampled_utc)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## 总结\n",
        "\n",
        "本教程全面介绍了Pandas时间序列重采样的核心知识点：\n",
        "\n",
        "1. **重采样基础**：理解上采样和下采样的概念\n",
        "2. **resample()方法**：掌握基本语法和参数\n",
        "3. **下采样**：使用聚合函数将高频数据转为低频数据\n",
        "4. **上采样**：使用填充方法将低频数据转为高频数据\n",
        "5. **聚合函数**：内置和自定义聚合函数的使用\n",
        "6. **分组重采样**：结合groupby()进行分组重采样\n",
        "7. **边界控制**：label和closed参数的使用\n",
        "8. **频率字符串**：各种频率字符串的含义\n",
        "9. **实际应用**：股票、传感器、销售等场景\n",
        "10. **性能优化**：提高重采样效率的技巧\n",
        "\n",
        "重采样是时间序列分析的基础操作，掌握这些知识点对于数据分析工作非常重要！\n"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
